// Copyright (c) Microsoft Corporation 2005-2007.
// This sample code is provided "as is" without warranty of any kind. 
// We disclaim all warranties, either express or implied, including the 
// warranties of merchantability and fitness for a particular purpose. 
//

#light

module Microsoft.FSharp.Data.Linq

#nowarn "57"

open System
open System.Linq
open System.Collections.Generic
open System.Linq.Expressions
open System.Data.Linq
open System.Reflection
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Raw
open Microsoft.FSharp.Quotations.Typed
open List

//-------------------------------------------------------------------------

let inLang lang e = List.exists (fun m -> match e with GenericTopDefnUse m x -> true | _ -> false) lang
let deepMacroExpandUntilInLang lang e = Raw.DeepMacroExpandUntil (inLang lang) e

// This is the language the translation understands, addition to 
// primitive F# constructs like tuples, if-then-else, &&, ||, as well as the calls to 
// .NET methods and properties that the DLINQ library comprehends.
// All uses of macros must boil down to uses of these operators.  For example, the
// operators on Nullable above boil down to these operators.
let LINQ = [ (<@@ (+) @@>); 
             (<@@ (-) @@>); 
             (<@@ ( * ) @@>); 
             (<@@ (%) @@>); 
             (<@@ (/) @@>); 
             (<@@ not @@>); 
             (<@@ (>) @@>); 
             (<@@ (<) @@>); 
             (<@@ (>=) @@>); 
             (<@@ (<=) @@>); 
             (<@@ (=) @@>); 
             (<@@ (<>) @@>); 
             (<@@ (~-) : int -> int @@>) ]

let expandMacros x = Typed.map_raw (deepMacroExpandUntilInLang LINQ) x

let asExpr x = (x :> Expression)

/// Convert F# quotations to Linq expression trees.
/// A more polished LINQ-Quotation translator will be published
/// concert with later versions of LINQ.
let rec exprToLinqExpression env inp = 
   //printf "exprToLinqExpression : %A\n" e;
    match inp with 
    | Var(v) -> Map.find v env
    | Equality(x1,x2)            -> Expression.Equal(exprToLinqExpression env x1, exprToLinqExpression env x2)  |> asExpr
    | Bool(x)                    -> Expression.Constant(box x)                 |> asExpr
    | Byte(x)                    -> Expression.Constant(box x)                 |> asExpr
    | SByte(x)                   -> Expression.Constant(box x)                 |> asExpr
    | Int16(x)                   -> Expression.Constant(box x)                 |> asExpr
    | UInt16(x)                  -> Expression.Constant(box x)                 |> asExpr
    | Int32(x)                   -> Expression.Constant(box x)                 |> asExpr
    | UInt32(x)                  -> Expression.Constant(box x)                 |> asExpr
    | Int64(x)                   -> Expression.Constant(box x)                 |> asExpr
    | UInt64(x)                  -> Expression.Constant(box x)                 |> asExpr
    | Single(x)                  -> Expression.Constant(box x)                 |> asExpr
    | Double(x)                  -> Expression.Constant(box x)                 |> asExpr
    | String(x)                  -> Expression.Constant(box x)                 |> asExpr
    | Char(x)                    -> Expression.Constant(box x)                 |> asExpr
    | LazyAnd(x1,x2)             -> Expression.And(exprToLinqExpression env x1, exprToLinqExpression env x2) |> asExpr
    | LazyOr(x1,x2)              -> Expression.Or(exprToLinqExpression env x1, exprToLinqExpression env x2)  |> asExpr
    | LiftedValue(x,ty)          -> Expression.Constant(box x,ty)              |> asExpr
    | Coerce(toTy,x)             -> Expression.Convert(exprToLinqExpression env x,toTy)          |> asExpr
    | Recd(recdTy,args) -> 
         let consl = recdTy.GetConstructors() |> Array.to_list 
         match consl with 
         | [cons] -> Expression.New(cons,(exprsToLinqExpressions env args : Expression[])) |> asExpr
         | _ -> failwith "too many constructors found on F# record type"
    | RecdGet(recdTy,recdFieldName,recdArg) -> 
         let prop = recdTy.GetProperty(recdFieldName)
         Expression.Property(exprToLinqExpression env recdArg, prop) |> asExpr
    | NewArray(ty,args) -> Expression.NewArrayInit(ty,exprsToLinqExpressions env args) |> asExpr
    | PropGet(propInfo,objArg) -> Expression.Property(exprToLinqExpression env objArg, propInfo) |> asExpr
    | FieldGet(fldInfo,objArg) -> Expression.Field(exprToLinqExpression env objArg, fldInfo) |> asExpr
    | CtorCall(ctorInfo,objArgs) -> 
        let objArgsP = map (exprToLinqExpression env) objArgs |> Array.of_list
        Expression.New(ctorInfo,objArgsP) |> asExpr
    | NewDelegate(dty,Lambdas(vs,b)) -> 
        let vsP = map exprVarToLinqParameter vs 
        let env = List.fold_right2 (fun (v:ExprVar) vP -> Map.add v.Name (vP |> asExpr)) vs vsP env 
        let bodyP = exprToLinqExpression env b 
        Expression.Lambda(dty, bodyP, vsP) |> asExpr 
    | MethodCall(meth,args) -> 
        let argsP = map (exprToLinqExpression env) args 
        if meth.IsStatic then 
          Expression.Call(null, meth, argsP) |> asExpr
        else 
          let (a,r) = match argsP with | a::r -> a,r | _ -> failwith "Can't call instance method without instance argument!"
          Expression.Call(a, meth, r) |> asExpr
    | Tuple(tupTy,args) -> 
         let cons = tupTy.GetConstructor(tupTy.GetGenericArguments()) 
         let argsP = exprsToLinqExpressions env args 
         //printf "efTupleMk: cons = %a, argR = %a\n" output_any cons output_any argsP;
         Expression.New(cons,argsP) |> asExpr
    | TupleGet(tupTy,n,arg) -> 
         let pname = sprintf "Item%d" (n + 1) 
         let prop = tupTy.GetProperty(pname) 
         Expression.Property(exprToLinqExpression env arg, prop) |> asExpr
    | GenericTopDefnApp <@@ ( + ) @@>([ty1;ty2;ty3],[x1;x2]) when (ty1 = (type string)) && (ty2 = (type string)) ->
         exprToLinqExpression env (<@@  System.String.Concat( [| _ ; _ |] : string array ) @@> x1 x2)
    | GenericTopDefnApp <@@ ( = ) @@>([ty1],[x1;x2]) when (ty1 = (type string)) ->
         exprToLinqExpression env (<@@  System.String.op_Equality(_,_) @@> x1 x2)
    | TopDefnApp <@@ ( + ) @@> [x1;x2] -> Expression.Add(exprToLinqExpression env x1, exprToLinqExpression env x2)      |> asExpr
    | TopDefnApp <@@ ( = ) @@> [x1;x2] -> Expression.Equal(exprToLinqExpression env x1, exprToLinqExpression env x2)       |> asExpr
    | TopDefnApp <@@ ( / ) @@> [x1;x2] -> Expression.Divide (exprToLinqExpression env x1, exprToLinqExpression env x2)  |> asExpr
    | TopDefnApp <@@ ( - ) @@> [x1;x2] -> Expression.Subtract(exprToLinqExpression env x1, exprToLinqExpression env x2) |> asExpr
    | TopDefnApp <@@ ( * ) @@> [x1;x2] -> Expression.Multiply(exprToLinqExpression env x1, exprToLinqExpression env x2) |> asExpr
    | TopDefnApp <@@ ( ~-) : int -> int @@> [x1]    -> Expression.Negate(exprToLinqExpression env x1)                                |> asExpr
    | TopDefnApp <@@ ( > ) @@> [x1;x2] -> Expression.GreaterThan(exprToLinqExpression env x1, exprToLinqExpression env x2)       |> asExpr
    | TopDefnApp <@@ ( >=) @@> [x1;x2] -> Expression.GreaterThanOrEqual(exprToLinqExpression env x1, exprToLinqExpression env x2)       |> asExpr
    | TopDefnApp <@@ ( <)  @@> [x1;x2] -> Expression.LessThan(exprToLinqExpression env x1, exprToLinqExpression env x2)       |> asExpr
    | TopDefnApp <@@ ( <=) @@> [x1;x2] -> Expression.LessThanOrEqual(exprToLinqExpression env x1, exprToLinqExpression env x2)       |> asExpr
    | TopDefnApp <@@ ( <>) @@> [x1;x2] -> Expression.NotEqual(exprToLinqExpression env x1, exprToLinqExpression env x2)       |> asExpr
    | TopDefnApp <@@ not   @@> [x1]    -> Expression.Not(exprToLinqExpression env x1)                                   |> asExpr
    | _ -> 
        failwithf "Could not convert the following F# Quotation to a LINQ Expression Tree\n--------\n%A\n-------------\n" inp;

and exprsToLinqExpressions env es = es |> List.map (exprToLinqExpression env) |> Array.of_list 

and exprVarToLinqParameter (v: ExprVar) = 
    let ty = v.Type
    let nm = v.Name.Text 
    //printf "** Expression .Parameter(%a, %a)\n" output_any ty output_any nm;
    Expression .Parameter(ty, nm)

  
//-------------------------------------------------------------------------
// F# - DLinq operators

let getGenericMethodDefinition (m:MethodInfo) = 
    if m.IsGenericMethod then m.GetGenericMethodDefinition() else m

/// (fun (x,y) -> z) is represented as 'fun p -> let x = p#0 let y = p#1' etc.
/// This reverses this encoding.
let (|TupledLambda|_|) lam =
    /// Strip off the 'let' bindings for an TupledLambda
    let rec stripSuccessiveProjLets (p:ExprVar) n expr =
        match expr with 
        | Let((v1,TupleGet(_,m,Var(pA))),rest) 
              when p.Name = pA && m = n-> 
                  let restvs,b = stripSuccessiveProjLets p (n+1) rest
                  v1::restvs, b
        | _ -> ([],expr)
    match lam with 
    | Lambdas([p],body) ->
          match stripSuccessiveProjLets p 0 body with 
          | [],b -> Some([p], b)
          | letvs,b -> Some(letvs,b)
    | _ -> None

let findGenericStaticMethodInfo mexpr =
    match mexpr with 
    | TupledLambda(_,MethodCall(methInfo,_)) -> getGenericMethodDefinition methInfo
    | _ -> failwithf "findGenericStaticMethodInfo: %A is not a method call lambda" mexpr

let callGenericStaticMethod mexpr  =
    let m = findGenericStaticMethodInfo mexpr in
    //printf "m = %A\n" m;
    fun (tyargs,args) -> 
        //printf "invoking %A\n" m;
        
        let m = m.MakeGenericMethod(Array.of_list tyargs) 
        m.Invoke(null,Array.of_list args)

let QT = (type System.Linq.Queryable)
let ST = (type System.Linq.Enumerable)
let EGT = (type System.Linq.Expressions.Expression<int>).GetGenericTypeDefinition()
let ET = (type System.Linq.Expressions.Expression)
let FT1 = (type System.Func<int,int>).GetGenericTypeDefinition()
let FT2 = (type System.Func<int,int,int>).GetGenericTypeDefinition()
let boolTy = (type bool)
let mkQueryFuncTy (dty,rty) = FT1.MakeGenericType([| dty; rty |])
let mkQueryFunc2Ty (dty1,dty2,rty) = FT2.MakeGenericType([| dty1; dty2; rty |])

let IET = (type IEnumerable<int>).GetGenericTypeDefinition()
let IQT = (type IQueryable<int>).GetGenericTypeDefinition()
let mkIEnumerableTy dty= IET.MakeGenericType([| dty|])
let mkIQueryableTy dty= IQT.MakeGenericType([| dty|])

let callQueryableSelectMany = 
    let F = callGenericStaticMethod <@@  System.Linq.Queryable.SelectMany : _ * Expression<Func<_,_>> -> _ @@> 
    fun (srcTy,targetTy,src,selector:Expression)  -> 
        F ([srcTy;targetTy],[src;box selector])

let callQueryableOrderBy = 
    let F = callGenericStaticMethod <@@ System.Linq.Queryable.OrderBy : _ * Expression<Func<_,_>> -> _ @@> 
    fun (srcTy,keyTy,src,keySelector:Expression)  -> 
        F ([srcTy;keyTy],[src;box keySelector])

let callQueryableGroupBy = 
    let F = callGenericStaticMethod <@@ System.Linq.Queryable.GroupBy : _ * Expression<Func<_,_>> -> _ @@> 
    fun (srcTy,keyTy,src,keySelector:Expression)  -> 
        F ([srcTy;keyTy],[src;box keySelector])

let callQueryableGroupByAndProject = 
    let F = callGenericStaticMethod <@@ System.Linq.Queryable.GroupBy : _ * Expression<Func<_,_>> * Expression<Func<_,_>> -> _ @@> 
    fun (srcTy,keyTy,resultTy,src,keySelector:Expression,resultSelector:Expression)  -> 
        F ([srcTy;keyTy;resultTy],[src;box keySelector;box resultSelector])

let callQueryableGroupJoin = 
    let F = callGenericStaticMethod <@@ System.Linq.Queryable.GroupJoin : _ * _ * _ * _ * _  -> _ @@> 
    fun (outerSrcTy,innerSrcTy,keyTy,resultTy,outer,inner,outerKeySelector:Expression,innerKeySelector:Expression,resultSelector:Expression)  -> 
        F ([outerSrcTy;innerSrcTy;keyTy;resultTy],
           [outer;inner;box outerKeySelector;box innerKeySelector;box resultSelector])


let callQueryableSelect = 
    let F = callGenericStaticMethod <@@ System.Linq.Queryable.Select : _ * Expression<Func<_,_>> -> _ @@> 
    fun (srcTy,targetTy,src,selector:Expression) -> 
        F ([srcTy;targetTy],[src;box selector])

let callQueryableWhere = 
    let F = callGenericStaticMethod <@@ System.Linq.Queryable.Where : _ * Expression<Func<_,_>> -> _ @@> 
    fun (srcTy,src,predicate:Expression) -> 
        F ([srcTy],[src;box predicate])

let callQueryableFirst = 
    let F = callGenericStaticMethod <@@ System.Linq.Queryable.First : _ * Expression<Func<_,_>> -> _ @@> 
    fun (srcTy,src,predicate:Expression) -> 
        F ([srcTy],[src;box predicate])

let callQueryableTake = 
    let F = callGenericStaticMethod <@@ System.Linq.Queryable.Take @@> 
    fun (srcTy,src,n:int32) -> 
        F ([srcTy],[src;box n])

let callQueryableDistinct = 
    let F = callGenericStaticMethod <@@ System.Linq.Queryable.Distinct @@> 
    fun (srcTy,src) -> 
        F ([srcTy],[src])

let bindGenericMethod (methInfo:MethodInfo) tyargs = 
    if methInfo.IsGenericMethod then 
        methInfo.GetGenericMethodDefinition().MakeGenericMethod(Array.of_list tyargs)
    else
        methInfo
        
let mkGenericStaticMethod lam  =
    // Nb. the SelectMany/Where/Select methods theoretically expects an expression, but the
    // LINQ team decided to only pass it a delegate construction. The coercion from
    // the delegate construction to the expression is effectively implicit in LINQ, but
    // not in F# quotations, so we have to use 'Unchecked' version (see also FSBUGS #970)
    let methInfo = findGenericStaticMethodInfo lam 
    (fun (tyargs,args) -> Unchecked.MkMethodCall(bindGenericMethod methInfo tyargs,args))
       
let mkSelect = 
    let F = mkGenericStaticMethod <@@ (System.Linq.Queryable.Select : _ * Expression<Func<_,_>> -> _) @@>
    fun (srcTy,targetTy,src,f)  -> 
        // REVIEW: LINQ likes to see a coercion to an interface type
        // at this point. F# eliminate many coercions from the tree during
        // type checking. We should probably stop doing this.
        F ([srcTy;targetTy],[MkCoerce(mkIQueryableTy srcTy,src);Unchecked.MkNewDelegate(mkQueryFuncTy(srcTy,targetTy), f)])

let mkSelectMany = 
    let F = mkGenericStaticMethod <@@ (System.Linq.Queryable.SelectMany : IQueryable<_> * Expression<Func<_,_>> -> IQueryable<_>) @@>
    fun (srcTy,targetTy,src,f)  -> 
        // REVIEW: LINQ likes to see a coercion to an interface type
        // at this point. F# eliminate many coercions from the tree during
        // type checking. We should probably stop doing this.
        F ([srcTy;targetTy],[MkCoerce(mkIQueryableTy srcTy,src);Unchecked.MkNewDelegate(mkQueryFuncTy(srcTy,targetTy), f)])

let mkQueryableWhere = 
    let F = mkGenericStaticMethod <@@ (System.Linq.Queryable.Where : _ * Expression<Func<_,_>> -> _) @@>
    fun (srcTy,src,f)  -> 
        // REVIEW: LINQ likes to see a coercion to an interface type
        // at this point. F# eliminate many coercions from the tree during
        // type checking. We should probably stop doing this.
        F ([srcTy],[MkCoerce(mkIQueryableTy srcTy,src);Unchecked.MkNewDelegate(mkQueryFuncTy(srcTy,(type bool)), f)])

let mkLambda = 
    let F = callGenericStaticMethod <@@ (fun (a:Expression,b:ParameterExpression[]) -> System.Linq.Expressions.Expression.Lambda<_>(a,b)) @@> 
    fun (srcTy,targetTy,arg:Expression,p:ParameterExpression) -> 
        (F ([mkQueryFuncTy(srcTy,targetTy)],[box arg;box [| p |] ]) :?> Expression)

let mkLambda2 = 
    let F = callGenericStaticMethod <@@ (fun (a:Expression,b:ParameterExpression[]) -> System.Linq.Expressions.Expression.Lambda<_>(a,b)) @@> 
    fun (srcTy1,srcTy2,targetTy,arg:Expression,p1:ParameterExpression,p2:ParameterExpression) -> 
        (F ([mkQueryFunc2Ty(srcTy1,srcTy2,targetTy)],[box arg;box [| p1; p2 |] ]) :?> Expression)

let mkExprTy ety = EGT.MakeGenericType([| ety |])

// F# 'when' clauses in sequence expressions go to singleton/empty
// However they always appear after a 'for'.
// This helps eliminate these by matching
//     Seq.map_concat (fun x -> if P[x] then Seq.singleton E[x] else Seq.empty)  sq 
let (|EncodedMapFilter|_|) = 
    function | GenericTopDefnApp
                  <@@ Seq.map_concat @@>
                  ([srcTy;_;targetTy;_],
                   [Lambdas([v], Cond(Equality(Bool(true),gd), 
                                      TopDefnApp <@@ Seq.singleton @@>[res], 
                                      TopDefnApp <@@ Seq.empty @@>(_)));
                    sq]) -> Some(srcTy,targetTy,v,gd,res,sq)
             | _ -> None

// F# '->' yields in sequence expressions appear as Seq.singleton.
// But Seq.singleton is normally eliminated as F# always represents 
// "Seq.map_concat(fun x -> Seq.singleton(E[x])) sq" as "Seq.map (fun x -> E[x]) sq".
// However when 'let' is present 'singleton' can't be eliminated like this.
// However LINQ rewriting eliminates 'let' by substitution, allowing us to eliminate 
// the singletons.
//
// Thus we match 
//     Seq.map_concat (fun x -> Seq.singleton(E[x]))  sq 
let (|EncodedMap|_|) = 
    function | GenericTopDefnApp
                  <@@ Seq.map_concat @@>
                  ([srcTy;_;targetTy;_],
                   [Lambdas([v], TopDefnApp <@@ Seq.singleton @@>[res]);
                    sq]) -> Some(srcTy,targetTy,MkLambda(v,res),sq)
             | _ -> None


let (|ThisVar|_|) dbv e = match e with Var v when v = dbv -> Some() | _ -> None

let (|GenericMethodDefnUse|_|) mexpr = 
    let F = findGenericStaticMethodInfo mexpr
    assert (F.IsGenericMethodDefinition);
    function MethodCall(info,args) when getGenericMethodDefinition info = F -> Some(info,args)
           | _ -> None

let (|GenericMethodInfoApp|) (m: MethodInfo) = 
    if m.IsGenericMethod then (m.GetGenericMethodDefinition(), m.GetGenericArguments()) else (m, [| |])
    
let debug = false

let SQL (p : Expr<'a>) : 'a = 
    let showf fmt = 
        Printf.ksprintf (fun s -> if debug then System.Windows.Forms.MessageBox.Show(s) |> ignore  ) fmt

    let p = expandMacros p 
    let body = to_raw p
    let env = Map.empty
    let rec transInner tm = 
        showf "transInner:\n%A" tm;
        if debug then printf "--> transInner, tm = %A\n" tm
        match tm with 
        | Coerce (ty,expr) -> 
            MkCoerce(ty,transInner expr)            
            
        // REVIEW: Some sample LINQ queries return tuples or anonymous
        // records which contain further queries. We have to rewrite 
        // the F# comprehension notation out of these.
        //
        // It would be interesting to see a spec somewhere that listed this sort of thing.
        | Tuple(ty,exprs) -> Unchecked.MkTuple(ty,map transInner exprs)
        | Recd(ty,exprs) -> MkRecd(ty,map transInner exprs)
        | Sum(ty,tag,exprs) -> MkSum(ty,tag,map transInner exprs)
            
        (* Seq.map_concat (fun x -> if P x then Seq.singleton x else Seq.empty)  sq @@> 
           ~~> [|sq|].Where(gd) *)
        | EncodedMapFilter(srcTy,targetTy,v,gdBody,resBody,sq) ->
            let gd = MkLambda(v, gdBody)
            let sq = mkQueryableWhere(srcTy,transInner sq,gd)
            
            match resBody with
            | Var(vA) when v.Name = vA -> 
                showf "rewrote encoded filter, sq = %A, v.Type = %A" sq v.Type;
                sq
            | _ -> 
                showf "rewrote encoded filter-map";
                let res = MkLambda(v, resBody)
                mkSelect(srcTy,targetTy, sq, res)

        | EncodedMap(srcTy,targetTy,res,sq) ->
            showf "rewrote inner encoded map";
            mkSelect(srcTy,targetTy, transInner sq, res)

        | GenericTopDefnApp <@@ Seq.map_concat @@> ([srcTy;_;targetTy;_],[Lambda(selectorVar,selectorBody);sq]) ->
            // REVIEW: LINQ likes to see a coercion to an interface type
            // at this point. F# eliminate many coercions from the tree during
            // type checking. We should probably stop doing this.
            let selector = MkLambda(selectorVar,MkCoerce(mkIEnumerableTy targetTy,selectorBody))
            mkSelectMany(srcTy,targetTy, transInner sq, selector )
        
        | GenericTopDefnApp <@@ Seq.map @@> ([srcTy;targetTy;_],[f;sq]) ->
    
            if debug then printf "--> transInner, map\n" 
            mkSelect(srcTy,targetTy, transInner sq, f)

        // These occur in the F# quotation form of F# sequence expressions            
        | GenericTopDefnApp <@@ seq @@> (_,[sq]) ->
    
            if debug then printf "--> transInner, seq ... \n" 
            transInner sq

        // These occur in the F# quotation form of F# sequence expressions            
        | GenericTopDefnApp <@@ Seq.delay @@> (_,[Lambda(_,sq)]) ->
    
            if debug then printf "--> transInner, delay\n" 
            transInner sq


        | tm  -> tm in 

    /// Project F# function expressions to Linq LambdaExpression nodes
    let funcExprToLinqFunc2Expression (srcTy,targetTy,e) = 
        match e with 
        | Lambdas([v],body) -> 
            let vP = exprVarToLinqParameter v 
            let env = Map.add v.Name (vP |> asExpr) env
            if debug then printf "--> funcExprToLinqFunc2Expression, body = %A\n" body
            let bodyT = transInner body
            showf "transInner, inp = \n%A\n res = \n%A" body bodyT;
            if debug then printf "--- funcExprToLinqFunc2Expression, body = %A\n" body
            let bodyP = exprToLinqExpression env bodyT
            if debug then printf "<-- funcExprToLinqFunc2Expression, bodyP = %A\n" bodyP
            mkLambda(srcTy,targetTy,bodyP, vP)
        | _ -> failwith "QUOTE: Could not project (an explicit lambda must be provided)"
        
//    let x : IQueryable<_> = failwith ""
//    let y : System.Data.Linq.Provider. = failwith ""
    
    let funcExprToLinqFunc3Expression (srcTy1,srcTy2,targetTy,e) = 
        match e with 
        | Lambdas([v1;v2],body) -> 
            let v1P = exprVarToLinqParameter v1 
            let v2P = exprVarToLinqParameter v2 
            let env = Map.add v1.Name (v1P |> asExpr) env
            let env = Map.add v2.Name (v2P |> asExpr) env
            if debug then printf "--> funcExprToLinqFunc3Expression , body = %A\n" body
            let bodyT = transInner body
            showf "transInner, inp = \n%A\n res = \n%A" body bodyT;
            if debug then printf "--- funcExprToLinqFunc3Expression , body = %A\n" body
            let bodyP = exprToLinqExpression env bodyT
            if debug then printf "<-- funcExprToLinqFunc3Expression , bodyP = %A\n" bodyP
            mkLambda2(srcTy1,srcTy2,targetTy,bodyP,v1P,v2P)
        | _ -> failwith "QUOTE: Could not project (an explicit lambda must be provided)"
      

    let rec transOuter tm = 
        showf "transOuter:\n%A" tm;
        if debug then printf "--> transOuter, tm = %A\n" tm
        match tm with 
            
        | Coerce (ty,expr) -> 
            transOuter expr
            
        | LiftedValue(obj,_) -> obj
            
        | PropGet(propInfo,expr) -> 
            let obj = transOuter expr
            if debug then printf "--> transOuter, property\n" 
            let res = propInfo.GetValue(obj,[| |]) 
            res
            
        | FieldGet(fldInfo,expr) -> 
            let obj = transOuter expr
            if debug then printf "--> transOuter, field\n" 
            let res = fldInfo.GetValue(obj) 
            if debug then printf "<-- transOuter, field\n" 
            res

        // LINQ likes to see outer filters as a '.Where'. 
        | EncodedMapFilter(srcTy,targetTy,v,gd,res,sq) ->
            showf "rewrote outer encoded filter";
            let gd = MkLambda(v, gd)
            let sq = callQueryableWhere(srcTy, transOuter sq, funcExprToLinqFunc2Expression (srcTy,boolTy,gd))
            match res with
            | Var(vA) when v.Name = vA -> sq
            | _ -> 
                let res = MkLambda(v, res)
                callQueryableSelect(srcTy,targetTy, sq, funcExprToLinqFunc2Expression (srcTy,targetTy,res))
            
        // Unsurprisingly LINQ likes to see map_concat(singleton) as a map 
        | EncodedMap(srcTy,targetTy,res,sq) ->
            showf "rewrote outer encoded map";
            callQueryableSelect(srcTy,targetTy, transOuter sq, funcExprToLinqFunc2Expression (srcTy,targetTy,res))            

        // These occur in the F# quotation form of F# sequence expressions            
        | GenericTopDefnApp <@@ Seq.map_concat @@> ([srcTy;_;targetTy;_],[Lambda(selectorVar,selectorBody);sq]) ->
            
            let sq = transOuter sq 
            // REVIEW: LINQ likes to see a coercion to an interface type
            // at this point. F# eliminate many coercions from the tree during
            // type checking. We should probably stop doing this.
            let selector = MkLambda(selectorVar,MkCoerce(mkIEnumerableTy(targetTy),selectorBody))
            let selectorL = funcExprToLinqFunc2Expression (srcTy,mkIEnumerableTy(targetTy),selector)
            showf "--> transOuter, map_concat, srcTy = %A, targetTy= %A, selectorVar.Type = %A, selector =\n%A\n, selectorL =\n%A\n" srcTy targetTy selectorVar.Type selector selectorL
            callQueryableSelectMany(srcTy,targetTy, sq, selectorL)
        
        // These occur in the F# quotation form of F# sequence expressions            
        | GenericTopDefnApp <@@ Seq.map @@> ([srcTy;targetTy;_],[f;sq]) ->
    
            if debug then printf "--> transOuter, map\n" 
            callQueryableSelect(srcTy,targetTy, transOuter sq, funcExprToLinqFunc2Expression (srcTy,targetTy,f))

        // These occur in the F# quotation form of F# sequence expressions            
        | GenericTopDefnApp <@@ Seq.delay @@> (_,[Lambda(_,body)]) ->
    
            if debug then printf "--> transOuter, delay\n" 
            transOuter body

        // These occur in the F# quotation form of F# sequence expressions            
        | GenericTopDefnApp <@@ seq @@> (_,[body]) ->
    
            if debug then printf "--> transOuter, delay\n" 
            transOuter body

        // These occur in the F# quotation form of F# sequence expressions            
        | GenericTopDefnApp <@@ Seq.filter @@> ([srcTy;_],[f;sq]) ->

            if debug then printf "--> transOuter, filter\n" 
            callQueryableWhere(srcTy, transOuter sq, funcExprToLinqFunc2Expression (srcTy,boolTy,f))

        // Translate uses of System.Linq.Enumerable.* operations 
        // to System.Linq.Queryable.* equivalents.
        | GenericMethodDefnUse 
                 <@@ System.Linq.Enumerable.Select : _ * Func<_,_> -> _ @@>
                 (GenericMethodInfoApp(ginfo,[| srcTy; targetTy |]),
                  [sq; NewDelegate(_,f)]) ->
            callQueryableSelect(srcTy,targetTy, transOuter sq, funcExprToLinqFunc2Expression (srcTy,targetTy,f))

        | GenericMethodDefnUse 
                 <@@ System.Linq.Enumerable.SelectMany : _ * Func<_,_> -> _ @@>
                 (GenericMethodInfoApp(ginfo,[| srcTy; targetTy |]),
                  [sq; NewDelegate(_,f)]) ->
            callQueryableSelectMany(srcTy,targetTy, transOuter sq, funcExprToLinqFunc2Expression (srcTy, mkIEnumerableTy(targetTy),f))

        | GenericMethodDefnUse 
                 <@@ System.Linq.Enumerable.OrderBy : _ * Func<_,_> -> _ @@>
                 (GenericMethodInfoApp(ginfo,[| srcTy; keyTy |]),
                  [sq; NewDelegate(_,f)]) ->
            callQueryableOrderBy(srcTy,keyTy, transOuter sq, funcExprToLinqFunc2Expression (srcTy,keyTy,f))

        | GenericMethodDefnUse 
                 <@@ System.Linq.Enumerable.First : _ * Func<_,_> -> _ @@>
                 (GenericMethodInfoApp(ginfo,[| srcTy |]),
                  [sq; NewDelegate(_,f)]) ->
            callQueryableFirst(srcTy, transOuter sq, funcExprToLinqFunc2Expression (srcTy,boolTy,f))

        | GenericMethodDefnUse 
                 <@@ System.Linq.Enumerable.GroupBy : _ * Func<_,_> -> _ @@>
                 (GenericMethodInfoApp(ginfo,[| srcTy; keyTy |]),
                  [source; NewDelegate(_,keySelector)]) ->
            callQueryableGroupBy(srcTy,keyTy, transOuter source, funcExprToLinqFunc2Expression (srcTy,keyTy,keySelector))

        | GenericMethodDefnUse 
                 <@@ System.Linq.Enumerable.GroupBy : _ * Func<_,_> * Func<_,_> -> _ @@>
                 (GenericMethodInfoApp(ginfo,[| srcTy; keyTy;resultTy |]),
                  [source; NewDelegate(_,keySelector); NewDelegate(_,resultSelector)]) ->
            callQueryableGroupByAndProject(srcTy,keyTy, resultTy,
                                           transOuter source, 
                                           funcExprToLinqFunc2Expression (srcTy,keyTy,keySelector),
                                           funcExprToLinqFunc2Expression (srcTy,resultTy,resultSelector))

        | GenericMethodDefnUse 
                 <@@ System.Linq.Enumerable.GroupJoin  : _ * _ * _ * _ * _  -> _ @@>
                 (GenericMethodInfoApp(ginfo,[| outerSrcTy; innerSrcTy; keyTy;resultTy |]),
                  [outer; inner; NewDelegate(_,outerKeySelector); NewDelegate(_,innerKeySelector); NewDelegate(_,resultSelector)]) ->
            callQueryableGroupJoin(outerSrcTy,innerSrcTy,keyTy,resultTy,
                                   transOuter outer, 
                                   transOuter inner,
                                   funcExprToLinqFunc2Expression (outerSrcTy,keyTy,outerKeySelector),
                                   funcExprToLinqFunc2Expression (innerSrcTy,keyTy,innerKeySelector),
                                   funcExprToLinqFunc3Expression (outerSrcTy,mkIEnumerableTy(innerSrcTy),resultTy,resultSelector))

        | GenericMethodDefnUse 
                 <@@ System.Linq.Enumerable.Take @@>
                 (GenericMethodInfoApp(ginfo,[| srcTy |]),
                  [sq; nexpr]) ->
            match nexpr with 
            | Int32 n | LiftedValue ((:? int32 as n), _) -> callQueryableTake(srcTy, transOuter sq, n)
            | _ -> failwith "The expression %A is too complex to use as an argument to 'take' within a SQL query. Consider splicing in an integer computed outside the query?"

        | GenericMethodDefnUse 
                 <@@ System.Linq.Enumerable.Distinct @@>
                 (GenericMethodInfoApp(ginfo,[| srcTy |]),
                  [sq]) ->
            callQueryableDistinct(srcTy, transOuter sq)


(*

    let fmin (qf : ('a -> ('c :> IComparable<'c>)) Expr) (coll :> IQueryable<'a>) : 'c = 
      System.Linq.Queryable.Min(coll,lambdaExprToLinqExpression (expandMacros qf))

    let min (coll :> IQueryable<'a>) : 'c = 
      System.Linq.Queryable.Min(coll)

    let averagem (coll :> IQueryable<Decimal>) : Decimal = 
      System.Linq.Queryable.Average(coll :> IQueryable<Decimal>)

    let averageNm (coll :> IQueryable<nullableDecimal>) : nullableDecimal = 
      System.Linq.Queryable.Average(coll:> IQueryable<nullableDecimal>)


*)

        | _  -> failwithf "The following construct was used as the outer portion of an SQL meta-program query but is not recognised by the F#-to-LINQ query translator:\n%A\nExamine your query and compare it with sample queries, perhaps moving some of the query out of the meta-program to be executed on the client-side. If you think this construct should be a valid query construct that can be mapped to LINQ then consider extending the query translator and/or reporting this as an issue to the F# team." tm in
    
    let res = transOuter body 
    if debug then printf "SQL: res.GetType() = %O" (res.GetType())
    unbox res


                    

//-------------------------------------------------------------------------
// Nullable utilities for F#

/// This operator compares Nullable values with non-Nullable values using
/// structural comparison
[<ReflectedDefinition>]
let (>=?!) (x : Nullable<'a>) (y: 'a) = 
    x.HasValue && x.Value >= y

[<ReflectedDefinition>]
let (>?!) (x : Nullable<'a>) (y: 'a) = 
    x.HasValue && x.Value > y

[<ReflectedDefinition>]
let (<=?!) (x : Nullable<'a>) (y: 'a) = 
    not x.HasValue || x.Value <= y

[<ReflectedDefinition>]
let (<?!) (x : Nullable<'a>) (y: 'a) = 
    not x.HasValue || x.Value < y

[<ReflectedDefinition>]
let (=?!) (x : Nullable<'a>) (y: 'a) = 
    x.HasValue && x.Value = y

[<ReflectedDefinition>]
let (<>?!) (x : Nullable<'a>) (y: 'a) = 
    not x.HasValue or x.Value <> y

/// This overloaded operator divides Nullable values by non-Nullable values
/// using the overloaded operator "/".  Inlined to allow use over any type,
/// as this resolves the overloading on "/".
[<ReflectedDefinition>]
let inline (/?!) (x : Nullable<'a>) (y: 'a) = 
    if x.HasValue then new Nullable<'a>(x.Value / y)
    else x

/// This overloaded operator adds Nullable values by non-Nullable values
/// using the overloaded operator "+".  Inlined to allow use over any type,
/// as this resolves the overloading on "+".
[<ReflectedDefinition>]
let inline (+?!) (x : Nullable<'a>) (y: 'a) = 
    if x.HasValue then new Nullable<'a>(x.Value + y)
    else x

/// This overloaded operator adds Nullable values by non-Nullable values
/// using the overloaded operator "-".  Inlined to allow use over any type,
/// as this resolves the overloading on "-".
[<ReflectedDefinition>]
let inline (-?!) (x : Nullable<'a>) (y: 'a) = 
    if x.HasValue then new Nullable<'a>(x.Value - y)
    else x

/// This overloaded operator adds Nullable values by non-Nullable values
/// using the overloaded operator "*".  Inlined to allow use over any type,
/// as this resolves the overloading on "*".
[<ReflectedDefinition>]
let inline ( *?!) (x : Nullable<'a>) (y: 'a) = 
    if x.HasValue then new Nullable<'a>(x.Value * y)
    else x

/// This overloaded operator adds Nullable values by non-Nullable values
/// using the overloaded operator "%".  Inlined to allow use over any type,
/// as this resolves the overloading on "%".
[<ReflectedDefinition>]
let inline ( %?!) (x : Nullable<'a>) (y: 'a) = 
    if x.HasValue then new Nullable<'a>(x.Value % y)
    else x

type NDecimal = Nullable<System.Decimal>


